﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.Reflection;
using System.Diagnostics;

namespace CrusadeGmTool.Diagnostics
{
    /// <summary>
    /// A simple wrapper to log4net
    /// </summary>
    public class Logger : ILog
    {
        private static object _syncRoot = new object();
        static bool InitDone = false;
        private static int _level = StatusLevel.Debug;
        log4net.ILog log;
        protected string LoggerType;
        public event EventHandler<StatusUpdateEventArgs> StatusUpdate;

        #region Properties
        public static int Level
        {
            get { return _level; }
            set { _level = value; }
        }
        #endregion

        #region constructors

        public Logger(Type type)
            : this(type.ToString())
        {
        }

        public Logger(string loggertype)
        {
            LoggerType = loggertype;
            if (!InitDone)
            {
                string path = "log4net.config";
                if (File.Exists(path))
                {
                    log4net.Config.XmlConfigurator.ConfigureAndWatch(new System.IO.FileInfo(path));
                }
                else
                {
                    log4net.Config.XmlConfigurator.Configure();
                }
                InitDone = true;
            }
            log = log4net.LogManager.GetLogger(LoggerType);
        }

        #endregion

        public virtual void TraceFunction(params object[] functionArgs)
        {
            if (_level > StatusLevel.Debug)
                return;
            MethodBase method = GetCallingMethod();
            string args = CreateArgsString(method, functionArgs);
            Debug("Calling from {0}.{1}({2})", method.DeclaringType.Name, method.Name, args);
        }

        private string CreateArgsString(MethodBase method, object[] functionArgs)
        {
            StringBuilder sb = new StringBuilder();
            ParameterInfo[] parameters = method.GetParameters();

            for (int i = 0; i < functionArgs.Length && i < parameters.Length; i++)
            {
                if (functionArgs[i] is object[])
                {
                    object[] array = functionArgs[i] as object[];

                    for (int j = 0; j < array.Length; j++)
                    {
                        sb.AppendFormat("param{0} = {1}{2}", j, array[j], j + 1 < array.Length ? ", " : "");
                    }
                }
                else
                {
                    sb.AppendFormat("{0} = {1}{2}", parameters[i].Name, functionArgs[i].ToString(), i + 1 < functionArgs.Length ? ", " : "");
                }
            }
            return sb.ToString();
        }

        public virtual void Debug(object obj)
        {
            if (_level > StatusLevel.Debug)
                return;

            lock (_syncRoot)
            {
                OnStatusUpdate(this, new StatusUpdateEventArgs(StatusLevel.Debug, obj.ToString()));
                log.Debug(obj.ToString());
            }
        }

        public virtual void Debug(string message)
        {
            if (_level > StatusLevel.Debug)
                return;

            lock (_syncRoot)
            {
                OnStatusUpdate(this, new StatusUpdateEventArgs(StatusLevel.Debug, message));
                log.Debug(message);
            }
        }

        public virtual void Debug(string message, params object[] objects)
        {
            if (_level > StatusLevel.Debug)
                return;

            lock (_syncRoot)
            {
                OnStatusUpdate(this, new StatusUpdateEventArgs((object)null, StatusLevel.Debug, message, objects));
                log.DebugFormat(message, objects);
            }
        }

        public virtual void Info(object obj)
        {
            if (_level > StatusLevel.Info)
                return;

            lock (_syncRoot)
            {
                Utility.PushColor(ConsoleColor.White);
                OnStatusUpdate(this, new StatusUpdateEventArgs(StatusLevel.Info, obj.ToString()));
                log.Info(obj.ToString());
                Utility.PopColor();
            }
        }

        public virtual void Info(string message, params object[] objects)
        {
            if (_level > StatusLevel.Info)
                return;

            lock (_syncRoot)
            {
                Utility.PushColor(ConsoleColor.White);
                OnStatusUpdate(this, new StatusUpdateEventArgs((object)null, StatusLevel.Info, message, objects));
                log.InfoFormat(message, objects);
                Utility.PopColor();
            }
        }

        public virtual void Warn(object obj)
        {
            if (_level > StatusLevel.Warn)
                return;

            lock (_syncRoot)
            {
                Utility.PushColor(ConsoleColor.Yellow);
                OnStatusUpdate(this, new StatusUpdateEventArgs((object)null, StatusLevel.Warn, obj.ToString()));
                log.Warn(obj.ToString());
                Utility.PopColor();
            }
        }

        public virtual void Warn(string message)
        {
            if (_level > StatusLevel.Warn)
                return;

            lock (_syncRoot)
            {
                Utility.PushColor(ConsoleColor.Yellow);
                OnStatusUpdate(this, new StatusUpdateEventArgs((object)null, StatusLevel.Warn, message));
                log.Warn(message);
                Utility.PopColor();
            }
        }

        public virtual void Warn(string message, params object[] objects)
        {
            if (_level > StatusLevel.Warn)
                return;

            lock (_syncRoot)
            {
                Utility.PushColor(ConsoleColor.Yellow);
                OnStatusUpdate(this, new StatusUpdateEventArgs((object)null, StatusLevel.Warn, message, objects));
                log.WarnFormat(message, objects);
                Utility.PopColor();
            }
        }

        public virtual void Error(object obj)
        {
            if (_level > StatusLevel.Error)
                return;

            lock (_syncRoot)
            {
                Utility.PushColor(ConsoleColor.Red);
                OnStatusUpdate(this, new StatusUpdateEventArgs((object)null, StatusLevel.Error, obj.ToString()));
                log.Error(obj.ToString());
                Utility.PopColor();
            }
        }

        public virtual void Error(string message)
        {
            if (_level > StatusLevel.Error)
                return;

            lock (_syncRoot)
            {
                Utility.PushColor(ConsoleColor.Red);
                OnStatusUpdate(this, new StatusUpdateEventArgs((object)null, StatusLevel.Error, message));
                log.Error(message);
                Utility.PopColor();
            }
        }
        public virtual void Error(string message, params object[] objects)
        {
            if (_level > StatusLevel.Error)
                return;

            lock (_syncRoot)
            {
                Utility.PushColor(ConsoleColor.Red);
                OnStatusUpdate(this, new StatusUpdateEventArgs((object)null, StatusLevel.Error, message, objects));
                log.ErrorFormat(message, objects);
                Utility.PopColor();
            }
        }

        public virtual void Fatal(object obj)
        {
            if (_level > StatusLevel.Fatal)
                return;

            lock (_syncRoot)
            {
                Utility.PushColor(ConsoleColor.Red);
                OnStatusUpdate(this, new StatusUpdateEventArgs(StatusLevel.Error, obj.ToString()));
                log.Fatal(obj.ToString());
                Utility.PopColor();
            }
        }

        public virtual void Fatal(string message)
        {
            if (_level > StatusLevel.Fatal)
                return;

            lock (_syncRoot)
            {
                Utility.PushColor(ConsoleColor.Red);
                OnStatusUpdate(this, new StatusUpdateEventArgs(StatusLevel.Error, message));
                log.Fatal(message);
                Utility.PopColor();
            }
        }

        public virtual void Fatal(string message, params object[] objects)
        {
            if (_level > StatusLevel.Fatal)
                return;

            lock (_syncRoot)
            {
                Utility.PushColor(ConsoleColor.Red);
                OnStatusUpdate(this, new StatusUpdateEventArgs((object)null, StatusLevel.Error, message, objects));
                log.FatalFormat(message, objects);
                Utility.PopColor();
            }
        }

        protected virtual void OnStatusUpdate(object sender, StatusUpdateEventArgs e)
        {
            if (StatusUpdate != null)
                StatusUpdate(sender, e);
        }

        private static MethodBase GetCallingMethod()
        {
            return new StackFrame(2, false).GetMethod();
        }

        private static Type GetCallingType()
        {
            return new StackFrame(2, false).GetMethod().DeclaringType;
        }
    }
}
